#include "component.hh"
#include <fstream>
#include <iostream>
#include <sstream>

Apparatus::Apparatus(const std::string& filepath) :
    _components{}
{
    std::ifstream infile {filepath};
    std::string line {};

    while (std::getline(infile, line))
    {
        std::vector<std::string> names {this->splitName(line)};

        // Create
        auto iter {names.begin()};
        RCComponent component {this->create(std::move(*(iter++)))};
        // Connect 
        for (; iter != names.end(); ++iter)
        {
            RCComponent connectComponent {this->create(std::move(*iter))};
            component->connectWith(connectComponent);
            connectComponent->connectWith(component);
        }
    }
}

auto Apparatus::splitName(const std::string& line) -> std::vector<std::string>
{
    std::vector<std::string> names {};

    std::size_t colonPos {line.find(':')};
    names.emplace_back(line.substr(0, colonPos));

    std::size_t begin {colonPos + 2};
    std::size_t end {begin};

    while ((end = line.find(' ', begin)) != std::string::npos)
    {
        names.emplace_back(line.substr(begin, end - begin));
        begin = end + 1;
    }
    names.emplace_back(line.substr(begin, line.size() - begin));

    return names;
}

auto Apparatus::create(std::string name) -> RCComponent
{
    // Find exit?
    auto findRes {this->_components.find(name)};
    if (findRes != this->_components.end())
        return findRes->second;
    
    // Add a new component
    RCComponent component {std::make_shared<Component>(std::move(name))};
    this->_components.emplace(component->name(), component);
    return component;
}

auto Apparatus::showComponents() -> void
{
    for (const auto& kv : this->_components)
        std::cout << kv.second->toString() << std::endl;
}

Component::Component(std::string name) :
    _name{std::move(name)},
    _connects{}
{

}

auto Component::connectWith(RCComponent component) -> void
{
    this->_connects.emplace_back(component);
}

auto Component::toString() -> std::string
{
    std::stringstream ss {};
    ss << this->_name << ": ";
    for (RCComponent component : this->_connects)
        ss << component->name() << ' ';
    return ss.str();
}